/**
 * Fichier Cours.java : Définition d'une classe de base :
 * fr.unice.gnpt.core.Cours : Gestion des Cours
 * 
 * Auteurs : Groupe GNPT : Gastinel, Nicolas, Pellegrino, Trovato
 */
package fr.unice.gnpt.core;

import java.util.ArrayList;
import java.util.Collection;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Version;

/**
 * Classe de gestion des cours.
 * <p>
 * Un cours est caractérisé par un nom unique. Un certain nombre d'heures de
 * cours magistraux, de travaux dirigés et de travaux pratiques associés. Ainsi
 * qu'un nombre de groupes de travaux dirigés et pratiques.
 * 
 * @author gnpt
 * @version 0.0.2
 */
@Entity
@Table(name = "Cours_gnpt")
public class Cours implements Comparable<Cours> {
	/**
	 * Représente un numéro de version (incrémenté à chaque modification),
	 * utilisé (par JPA) pour savoir si l’état d’une entité a été modifié entre
	 * 2 moments différents.
	 */
	private int version;
	/**
	 * Nom du cours.
	 */
	private String nomCours;
	/**
	 * Nombre d'heures de Cours Magistral associées au cours.
	 */
	private int nbHeuresCM;
	/**
	 * Nombre d'heures de Travaux Dirigés associées au cours.
	 */
	private int nbHeuresTD;
	/**
	 * Nombre d'heures de Travaux Pratiques associées au cours.
	 */
	private int nbHeuresTP;
	/**
	 * Nombre de groupes de Travaux Dirigés associés au cours.
	 */
	private int nbGroupesTD;
	/**
	 * Nombre de groupes de Travaux Pratiques associés au cours.
	 */
	private int nbGroupesTP;
	/**
	 * Collection contenant les enseignements associés au cours.
	 */
	private Collection<Enseignement> enseignements;

	/**
	 * Constructeur par défaut pour la classe Cours.
	 */
	public Cours() {
		this.enseignements = new ArrayList<Enseignement>();
	}

	/**
	 * Constructeur de la classe Cours.
	 * 
	 * @param nomCours
	 *            une String indiquant le nom du cours.
	 * @param nbHeuresCM
	 *            un entier indiquant le nombre d'heures de cours.
	 * @param nbHeuresTD
	 *            un entier indiquant le nombre d'heures de TD.
	 * @param nbGroupesTD
	 *            un entier indiquant le nombre de groupes de TD.
	 * @param nbHeuresTP
	 *            un entier indiquant le nombres d'heures de TP.
	 * @param nbGroupesTP
	 *            un entier indiquant le nombre de groupes de TP.
	 */
	public Cours(String nomCours, int nbHeuresCM, int nbHeuresTD,
			int nbGroupesTD, int nbHeuresTP, int nbGroupesTP) {
		this();
		this.nomCours = nomCours;
		this.nbHeuresCM = nbHeuresCM;
		this.nbHeuresTD = nbHeuresTD;
		this.nbGroupesTD = nbGroupesTD;
		this.nbHeuresTP = nbHeuresTP;
		this.nbGroupesTP = nbGroupesTP;
	}

	/**
	 * Accesseur pour le nom du cours.
	 * 
	 * @return le nom du cours.
	 */
	@Id
	@Column(name = "nom", nullable = false, length = 63)
	public String getNomCours() {
		return this.nomCours;
	}

	/**
	 * Accesseur pour le nombre d'heures de cours.
	 * 
	 * @return le nombre d'heures de cours.
	 */
	@Column(name = "hCM")
	public int getNbHeuresCM() {
		return this.nbHeuresCM;
	}

	/**
	 * Accesseur pour le nombre d'heures de Travaux Dirigés.
	 * 
	 * @return le nombre d'heures de TD.
	 */
	@Column(name = "hTD")
	public int getNbHeuresTD() {
		return this.nbHeuresTD;
	}

	/**
	 * Accesseur pour le nombre d'heures de Travaux Pratiques.
	 * 
	 * @return le nombre d'heures de TP.
	 */
	@Column(name = "hTP")
	public int getNbHeuresTP() {
		return this.nbHeuresTP;
	}

	/**
	 * Accesseur pour le nombre de groupes de Travaux Dirigés.
	 * 
	 * @return le nombre de groupes de TD.
	 */
	@Column(name = "gTD")
	public int getNbGroupesTD() {
		return this.nbGroupesTD;
	}

	/**
	 * Accesseur pour le nombre de groupes de Travaux Pratiques.
	 * 
	 * @return le nombre de groupes de TP.
	 */
	@Column(name = "gTP")
	public int getNbGroupesTP() {
		return this.nbGroupesTP;
	}

	/**
	 * Accesseur pour les enseignements associés à l'enseignant.
	 * 
	 * @return les enseignements associés à l'enseignant.
	 */
	@OneToMany(cascade = CascadeType.PERSIST, mappedBy = "cours")
	public Collection<Enseignement> getEnseignements() {
		return this.enseignements;
	}

	/**
	 * Accesseur pour le numéro de version (incrémenté à chaque modification),
	 * utilisé (par JPA) pour savoir si l’état d’une entité a été modifié entre
	 * 2 moments différents.
	 * 
	 * @return le numéro de version.
	 */
	@Version
	public int getVersion() {
		return this.version;
	}

	/**
	 * Mutateur pour le numéro de version courant.
	 * 
	 * @param version
	 *            le nouveau numéro de version.
	 */
	public void setVersion(int version) {
		this.version = version;
	}

	/**
	 * Mutateur pour les enseignements associés à l'enseignant.
	 * 
	 * @param enseignements
	 *            les nouveaux enseignements associés à l'enseignant.
	 */
	public void setEnseignements(Collection<Enseignement> enseignements) {
		this.enseignements = enseignements;
	}

	/**
	 * Mutateur pour le nom du cours.
	 * 
	 * @param nomCours
	 *            le nouveau nom du cours.
	 */
	public void setNomCours(String nomCours) {
		this.nomCours = nomCours;
	}

	/**
	 * Mutateur pour le nombre d'heures de cours.
	 * 
	 * @param nbHeuresCM
	 *            le nouveau nombre d'heures de cours.
	 */
	public void setNbHeuresCM(int nbHeuresCM) {
		this.nbHeuresCM = nbHeuresCM;
	}

	/**
	 * Mutateur pour le nombre d'heures de TD.
	 * 
	 * @param nbHeuresTD
	 *            le nouveau nombre d'heures de TD.
	 */
	public void setNbHeuresTD(int nbHeuresTD) {
		this.nbHeuresTD = nbHeuresTD;
	}

	/**
	 * Mutateur pour le nombre d'heures de TP.
	 * 
	 * @param nbHeuresTP
	 *            le nouveau nombre d'heures de TP.
	 */
	public void setNbHeuresTP(int nbHeuresTP) {
		this.nbHeuresTP = nbHeuresTP;
	}

	/**
	 * Mutateur pour le nombre de groupes de TD.
	 * 
	 * @param nbGroupesTD
	 *            le nouveau nombre de groupes de TD.
	 */
	public void setNbGroupesTD(int nbGroupesTD) {
		this.nbGroupesTD = nbGroupesTD;
	}

	/**
	 * Mutateur pour le nombre de groupes de TP.
	 * 
	 * @param nbGroupesTP
	 *            le nouveau nombre de groupes de TP.
	 */
	public void setNbGroupesTP(int nbGroupesTP) {
		this.nbGroupesTP = nbGroupesTP;
	}

	/**
	 * Méthode permettant de savoir si un service peut être proposé en fonction
	 * de ceux déjà validés
	 * 
	 * @param service
	 *            Enseignement dont on souhaite tester la compatibilité avec les
	 *            enseignements déjà validé du Cours
	 * 
	 * @return Booléen Décrivant si le service est compatible avec les
	 *         enseignements déjà validé
	 */
	public boolean estPossible(Service service) {
		if (service instanceof TacheAdministrative)
			return true;

		if (service instanceof Enseignement) {
			if ((this.getNbGroupeTDRestant() - ((Enseignement) service)
					.getNbGroupesTDEnCharge()) >= 0)
				if ((this.getNbGroupeTPRestant() - ((Enseignement) service)
						.getNbGroupesTPEnCharge()) >= 0)
					if (!(this.cmEnCharge() && ((Enseignement) service)
							.isCMEnCharge())) { return true; }
		}
		return false;
	}

	/**
	 * Description complète du cours.
	 * 
	 * @return renvoie une description complète du Cours.
	 */
	public String fullToString() {

		if (getNomCours() == null)
			return "Cours non renseigné";

		StringBuffer outString = new StringBuffer();

		// Nom du cours
		if (getNomCours().matches("^[AEIOUHaeiouh]")) {
			outString.append("Cours d'" + getNomCours());
		} else {
			outString.append("Cours de " + getNomCours());
		}

		// Heures de cours
		if (getNbHeuresCM() > 0) {
			outString.append(": " + getNbHeuresCM() + " heures de cours");
		}

		// Heures de TD
		if (getNbHeuresTD() > 0) {
			outString.append(", " + getNbHeuresTD() + " heures de TD");
		}

		// Heures de TP
		if (getNbHeuresTP() > 0) {
			outString.append(", " + getNbHeuresTP() + " heures de TP");
		}

		// Groupes de TD
		if (getNbGroupesTD() > 0) {
			outString.append(", " + getNbGroupesTD() + " groupes de TD");
		}

		// Groupes de TP
		if (getNbGroupesTP() > 0) {
			outString.append(", " + getNbGroupesTP() + " groupes de TP");
		}

		return outString.toString();
	}

	/**
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		if (getNomCours() == null)
			return "Non renseigné";

		return this.getNomCours();
	}

	/**
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (obj instanceof Cours) {
			Cours c = (Cours) obj;
			return ((this.getNomCours().equalsIgnoreCase(c.getNomCours())));
		}
		return false;
	}

	/**
	 * Accesseur pour le nombre de groupes de TD restant (i.e. non validés par
	 * le coordonnateur) pour le cours courant.
	 * 
	 * @return Le nombre de groupe de TD restant.
	 */
	public int getNbGroupeTDRestant() {
		int ret = this.nbGroupesTD;
		for (Enseignement e : this.enseignements) {
			if (e.estValide())
				ret -= e.getNbGroupesTDEnCharge();
		}
		return ret;
	}

	/**
	 * Accesseur pour le nombre de groupes de TP restant (i.e. non validés par
	 * le coordonnateur) pour le cours courant.
	 * 
	 * @return Le nombre de groupe de TP restant.
	 */
	public int getNbGroupeTPRestant() {
		int ret = this.nbGroupesTP;
		for (Enseignement e : this.enseignements) {
			if (e.estValide())
				ret -= e.getNbGroupesTPEnCharge();
		}
		return ret;
	}

	/**
	 * @return true si un des enseignements validé du cours contient les cours
	 *         magistraux
	 */
	public boolean cmEnCharge() {
		for (Enseignement e : this.enseignements) {
			if (e.estValide() && e.isCMEnCharge())
				return true;
		}
		return false;
	}
	
	/**
	 * Cette méthode retourne pour le cours this, tous les Enseignements qui 
	 * sont en conflits 
	 * 
	 * @return Collection<Enseignement> de tous les conflits
	 */
	public Collection<Enseignement> getConflits(){
		Collection<Enseignement> collConflit = new ArrayList<Enseignement>();
		Collection<Enseignement> collConflitCM = new ArrayList<Enseignement>();
		Collection<Enseignement> collConflitTP = new ArrayList<Enseignement>();
		Collection<Enseignement> collConflitTD = new ArrayList<Enseignement>();
		int nb_gr_td = 0;
		int nb_gr_tp = 0;
		
		for (Enseignement e : this.enseignements){
			if (e.estValide()){
				nb_gr_td += e.getNbGroupesTDEnCharge();
			nb_gr_tp += e.getNbGroupesTPEnCharge();
			}
		}
		
		for (Enseignement e : this.enseignements){
			if (!e.estValide() && e.isCMEnCharge())
				collConflitCM.add(e);
			if (!e.estValide() && e.getNbGroupesTDEnCharge() > 0 && 
					(this.getNbGroupeTDRestant() - nb_gr_td < 0))
				collConflitTD.add(e);
			if (!e.estValide() && e.getNbGroupesTPEnCharge() > 0 && 
					(this.getNbGroupeTPRestant() - nb_gr_tp < 0))
				collConflitTP.add(e);
			
			if (collConflitCM.size() > 1)
				for (Enseignement es : collConflitCM)
				collConflit.add(es);
			
			if (collConflitCM.size() > 1)
				for (Enseignement es : collConflitTP)
				collConflit.add(es);
			
			if (collConflitCM.size() > 1)
				for (Enseignement es : collConflitTD)
				collConflit.add(es);
		}
		return collConflit;
}
 
		/**
		 * Cette méthode retourne pour le cours this, et l'enseignement passé en 
		 * paramètre tous les Enseignements qui sont en conflits 
		 * 
		 * @param e
		 *          L'enseignement avec lequel tester les conflits
		 * 
		 * @return Collection<Enseignement> de tous les conflits
		 */
	public Collection<Enseignement> getConflits(Enseignement e){
		Collection<Enseignement> collConflit = new ArrayList<Enseignement>();
		for (Enseignement ens : this.enseignements){
			if (e != ens){
				if (e.isCMEnCharge() && ens.isCMEnCharge())
					collConflit.add(ens);
				if ((e.getNbGroupesTDEnCharge() > 0)  && (ens.getNbGroupesTDEnCharge() > 0) &&
						(this.getNbGroupeTDRestant() - e.getNbGroupesTDEnCharge() - ens.getNbGroupesTDEnCharge()) < 0)
					collConflit.add(ens);
				if ((e.getNbGroupesTPEnCharge() > 0)  && (ens.getNbGroupesTPEnCharge() > 0) && 
						(this.getNbGroupeTPRestant() - e.getNbGroupesTPEnCharge() - ens.getNbGroupesTPEnCharge()) < 0)
					collConflit.add(ens);
			}
		}
		return collConflit;
	}
	
	/**
	 * Teste si l'enseignement passé en paramètre est en conflit avec les autres enseignements non validés
	 *  
	 * @param ens 
	 *        enseignement à tester
	 *
	 * @return
	 *      <code>true</code> si l'enseignement est en conflit
	 */
	public boolean estEnConflit(Enseignement ens){
		int nb_gr_td = 0;
		int nb_gr_tp = 0;
		boolean cm = false;
		
		for (Enseignement e : this.enseignements){
			if (!e.estValide() && e != ens){
				if (e.isCMEnCharge())
					cm = true;
				nb_gr_td += e.getNbGroupesTDEnCharge();
				nb_gr_tp += e.getNbGroupesTPEnCharge();
			}
		}
		
		if (ens.isCMEnCharge() && cm)
			return true;
		if (this.getNbGroupeTDRestant() - nb_gr_td - ens.getNbGroupesTDEnCharge() < 0)
			return true;
		if (this.getNbGroupeTPRestant() - nb_gr_td - ens.getNbGroupesTPEnCharge() < 0)
			return true;
		
		return false;
	}
	/**
	 * @see java.lang.Comparable#compareTo(java.lang.Object)
	 */
	@Override
	public int compareTo(Cours o) {
		return this.nomCours.compareTo(o.nomCours);
	}
}
